package com.ruoyi.game.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.log.Log;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.GameConst;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.GameEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.game.domain.bo.RoundNoBO;
import com.ruoyi.game.domain.entity.GamePick3Record;
import com.ruoyi.game.domain.entity.GameWingoRecord;
import com.ruoyi.game.service.IGamePick3RecordService;
import com.ruoyi.game.service.IGameWingoRecordService;
import com.ruoyi.game.service.IRoundService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 场次服务层实现
 */
@Service
public class RoundService implements IRoundService {

    @Resource
    private RedisCache redisCache;
    @Resource
    private IGameWingoRecordService gameWingoRecordService;
    @Resource
    private IGamePick3RecordService gamePick3RecordService;

    @Override
    public RoundNoBO getRoundNo(int gameType, int timeLimit) {
        return getRound(timeLimit);
    }

    @Override
    public AjaxResult checkRound(Date now,String reqRoundNo,int timeLimit,int gameType) {
        long nowReqoundNo = Long.parseLong(reqRoundNo);
        long round = Long.parseLong(getRound(timeLimit).getRoundNo());
        if (round>nowReqoundNo){
            return AjaxResult.error("场次已结束");
        }
        Logger logger = LoggerFactory.getLogger(this.getClass());
        logger.debug("now"+String.valueOf(nowReqoundNo)+"-------round"+String.valueOf(round));

        if (round==nowReqoundNo){
            Calendar  calendar = Calendar.getInstance();
            switch (timeLimit){
                case 60:
                   long  nowSecond = calendar.get(Calendar.SECOND);
                    if (nowSecond>=57){
                        return AjaxResult.error("场次无效");
                    }
                    break;
                case 180:
                    int currentMinute = calendar.get(Calendar.MINUTE);
                    // 判断是否为三分钟一轮回的最后一分钟
                    if ((currentMinute % 3) == 0) {
                        //当前分钟数是三分钟一轮回的最后一分钟
                        nowSecond = calendar.get(Calendar.SECOND);
                        if (nowSecond>=57){
                            return AjaxResult.error("场次无效");
                        }
                    }
                    break;
                case 300:
                     currentMinute = calendar.get(Calendar.MINUTE);
                    if ((currentMinute % 5) == 0 ) {
                        nowSecond = calendar.get(Calendar.SECOND);
                        if (nowSecond>=57){
                            return AjaxResult.error("场次无效");
                        }
                    }
                    break;
                case 600:
                    currentMinute = calendar.get(Calendar.MINUTE);
                    if ((currentMinute % 10) == 0 ) {
                        nowSecond = calendar.get(Calendar.SECOND);
                        if (nowSecond>=57){
                            return AjaxResult.error("场次无效");
                        }
                    }
                    break;
            }
        }

//        // 获取当天日期
//        String dayStr = DatePattern.NORM_DATE_FORMAT.format(now);
//        // 查询缓存有没有值
//        String curCacheKey = String.format(CacheConstants.GAME_CUR_ROUND_KEY, gameType,dayStr,timeLimit);
//        RoundNoBO roundNo = redisCache.getCacheObject(curCacheKey);
//        if(ObjectUtil.isNull(roundNo)){
//            return AjaxResult.error("场次无效");
//        }
//        if(roundNo.getEndTime() - now.getTime() < GameConst.SHUT_IN_TIME){
//            return AjaxResult.error("正在开奖,请参与下轮游戏");
//        }
//        if(roundNo.getEndTime() < now.getTime()){
//            return AjaxResult.error("场次已结束");
//        }
//        if(!reqRoundNo.equals(roundNo.getRoundNo())){
//            return AjaxResult.error("场次不一致");
//        }
        return AjaxResult.success();
    }

    private RoundNoBO getRound(int timeLimit){
        String date = DateUtils.dateTime();
        Calendar calendar = Calendar.getInstance();
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int issue = hour * 60 + minute;
        String typeNo = "";
        switch (timeLimit) {
            case 60:
                typeNo = "01";
                break;
            case 180:
                typeNo = "03";
                break;
            case 300:
                typeNo = "05";
                break;
            case 600:
                typeNo = "10";
                break;
            default:
                break;
        }
        String endIssue = date + typeNo + String.format("%04d", issue);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);

        return new RoundNoBO(endIssue, calendar.getTimeInMillis());

//        // 获取当天日期
//        String dayStr = DatePattern.NORM_DATE_FORMAT.format(now);
//        // 查询缓存有没有值
//        String curCacheKey = String.format(CacheConstants.GAME_CUR_ROUND_KEY,gameType,dayStr,timeLimit);
//        RoundNoBO roundNo = redisCache.getCacheObject(curCacheKey);
//        if(ObjectUtil.isNotNull(roundNo) && roundNo.getEndTime() > now.getTime()){
//            return roundNo;
//        }
//        // 获取当前缓存的集合
//        String cacheKey = String.format(CacheConstants.GAME_DAY_ROUND_LIST_KEY,gameType,dayStr,timeLimit);
//        // 获取当天的场次
//        List<RoundNoBO> roundNoList = redisCache.getCacheList(cacheKey);
//        if(CollUtil.isEmpty(roundNoList)){
//            if(!lockInitCache(cacheKey,now,gameType,timeLimit)){
//                return null;
//            }
//        }
//        // 遍历场次，过期的放到开奖队列
//        return lockGetRound(cacheKey,dayStr,gameType,timeLimit);
    }

    private RoundNoBO lockGetRound(String cacheKey,String dayStr,int gameType,int timeLimit){
        // 获取锁
        String lockKey = "lock:"+cacheKey;
        boolean isLock = redisCache.tryLock(lockKey,timeLimit);
        if(!isLock){
            return null;
        }
        try {
            List<RoundNoBO> roundNoList = redisCache.getCacheList(cacheKey);
            if(CollUtil.isEmpty(roundNoList)){
                return null;
            }

            for(RoundNoBO roundNo : roundNoList){
                if(roundNo.getEndTime() < System.currentTimeMillis()){
                    // 过期的移除掉
                    RoundNoBO waitOpen = redisCache.leftPop(cacheKey);
                    if(ObjectUtil.isNull(waitOpen)){
                        continue;
                    }
                    // 过期的扔到带开奖队列
                    String waitOpenCacheKey = String.format(CacheConstants.GAME_WAIT_OPEN_ROUND_LIST_KEY,gameType,timeLimit);
                    redisCache.rightPush(waitOpenCacheKey,waitOpen);
                }else{
                    // 查询缓存有没有值
                    String curCacheKey = String.format(CacheConstants.GAME_CUR_ROUND_KEY,gameType,dayStr,timeLimit);
                    redisCache.setCacheObject(curCacheKey,roundNo);
                    return roundNo;
                }
            }
            return null;
        }catch (Exception e){
            throw new RuntimeException("加载场次异常",e);
        }finally {
            redisCache.releaseLock(lockKey);
        }
    }

    private boolean lockInitCache(String cacheKey,Date day,int gameType,int timeLimit){
        String lockKey = "lock:"+cacheKey;
        boolean isLock = redisCache.tryLock(lockKey,timeLimit);
        if(!isLock){
            return false;
        }
        try {
            initAndCache(day,gameType,timeLimit);
            return true;
        }catch (Exception e){
            throw new RuntimeException("加锁加载缓存异常",e);
        } finally {
            redisCache.releaseLock(lockKey);
        }
    }

    private List<RoundNoBO> initAndCache(Date day,int gameType,int timeLimit){
        // 初始化当天场次
        List<RoundNoBO> roundNoList = init(day,timeLimit);
        // 添加到数据库
        if(GameEnum.GameTypeEnum.PICK3.getType() == gameType){
            List<GamePick3Record> dataList = roundNoList.stream().map(item ->{
                GamePick3Record temp = new GamePick3Record();
                temp.setRoundNo(item.getRoundNo());
                temp.setTimeLimit(timeLimit);
                return temp;
            }).collect(Collectors.toList());
            gamePick3RecordService.saveBatch(dataList);
        }else{
            List<GameWingoRecord> dataList = roundNoList.stream().map(item ->{
                GameWingoRecord temp = new GameWingoRecord();
                temp.setRoundNo(item.getRoundNo());
                temp.setTimeLimit(timeLimit);
                return temp;
            }).collect(Collectors.toList());
            gameWingoRecordService.saveBatch(dataList);
        }
        // 获取当天日期
        String dayStr = DatePattern.NORM_DATE_FORMAT.format(day);
        // 获取当前缓存的集合
        String cacheKey = String.format(CacheConstants.GAME_DAY_ROUND_LIST_KEY,gameType,dayStr,timeLimit);
        // 设置缓存
        redisCache.setCacheList(cacheKey,roundNoList);
        redisCache.expire(cacheKey,25, TimeUnit.HOURS);
        return roundNoList;
    }

    private List<RoundNoBO> init(Date day,int timeLimit){
        // 当天开始时间
        DateTime beginTime = DateUtil.beginOfDay(day);
        // 当天结束时间
        DateTime endTime = DateUtil.endOfDay(day);
        // 获取当天所有场次
        long[] dayAllRound = range(beginTime.getTime(),endTime.getTime(),timeLimit*1000);
        // 获取时间限制枚举
        GameEnum.TimeLimitEnum timeLimitEnum = GameEnum.TimeLimitEnum.getTimeLimitEnum(timeLimit);
        String roundDayStr = DateUtil.format(day,DatePattern.PURE_DATE_PATTERN);
        // 获取当前时间所属场次
        List<RoundNoBO> roundNoList = new ArrayList<>();
        for(int i = 0 ; i < dayAllRound.length ; i++){
            long roundTime = dayAllRound[i];
            // 需要缓存的场次编号
            String roundNo = getRoundNo(roundDayStr,timeLimitEnum.getMinute(),i);
            roundNoList.add(new RoundNoBO(roundNo,roundTime));
        }
        return roundNoList;
    }

    /**
     * 获取场次编号
     * @param dayStr 日期
     * @param timeLimitType 时间限制类型
     * @param curRoundIndex 当前场次索引
     * @return
     */
    private String getRoundNo(String dayStr,int timeLimitType,int curRoundIndex){
        String roundNo = dayStr + timeLimitType + String.format("%04d", curRoundIndex);
        return roundNo;
    }

    /**
     * 给定范围内的整数列表
     *
     * @param start 开始（包含）
     * @param stop  结束（包含）
     * @param step  步进
     * @return 整数列表
     */
    private long[] range(long start, long stop, int step){
        if(stop < start){
            return null;
        }else if(start == stop){
            return new long[]{start};
        }

        long size = (stop - start) / step + 1;
        long[] values = new long[(int)size];
        int index = 0;
        for (long i = start; (step > 0) ? i <= stop : i >= stop; i += step) {
            values[index] = i;
            index++;
        }
        return values;
    }
}
